package fr.nts.framework.core.task.impl;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import fr.nts.framework.core.commun.tool.TimeUtils;
import fr.nts.framework.core.task.SimpleTask;
import fr.nts.framework.core.task.SimpleTaskManager;
import fr.nts.framework.core.task.tool.SimpleTaskExecutors;

/**
 * Implémentation du service {@link SimpleTaskManager} utilisant un pool de thread classique.
 * 
 * @author sco
 * @version 1.0.0
 */
public class PooledThreadTaskManager implements SimpleTaskManager,
                                                InitializingBean, 
                                                DisposableBean{

	private final static Logger log = LoggerFactory.getLogger(PooledThreadTaskManager.class);
	
	/**
	 * Le nombre de thread dans le pool
	 */
	private int nbThreadsInPool = 20;
	
	/**
	 * Le pool de thread
	 */
	private SimpleTaskExecutors ntsExecutors = null;
	
	/*
	 * (non-Javadoc)
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	@Override
	public void afterPropertiesSet() throws Exception{
		ntsExecutors = new SimpleTaskExecutors(nbThreadsInPool);
		log.info("Le pool de thread a bien été démarré avec {} threads en parallèle.", nbThreadsInPool);
	}

	
	/**
	 * Lance une nouvelle tâche en arrière et renvoie le statut courrante de celle-ci.
	 * 
	 * @param task La tâche à lancer.
	 * @return Le satut actuelle de la tâche.
	 */
	public Future<Boolean> launchNewTask(SimpleTask<?> task){
		return ntsExecutors.addTask(task);
	}
	
	
	/*
	 * (non-Javadoc)
	 * @see fr.nts.framework.core.task.NTSTaskManager#launchNewTaskInBackground(fr.nts.framework.core.task.NTSTask)
	 */
	@Override
	public void launchNewTaskInBackground(SimpleTask<?> task){
		ntsExecutors.addSilentTask(task);
	}

	
	/*
	 * (non-Javadoc)
	 * @see fr.nts.framework.core.task.NTSTaskManager#launchNewTaskAndWait(fr.nts.framework.core.task.NTSTask)
	 */
	@Override
	public <T extends Object> T launchNewTaskAndWait(SimpleTask<T> task){
		long start = System.currentTimeMillis();
		Future<Boolean> result = ntsExecutors.addTask(task);
		
		try{
			if(Boolean.TRUE.equals(result.get())){
				long end = System.currentTimeMillis();
				
				log.debug("Temps écoulé pour executer la tâche {} ==> {}", 
     			          new Object[]{task, TimeUtils.getTime(start, end)});
				
				return task.getResult();
			}
		}catch(InterruptedException e){
			log.error("Erreur: ", e);
		}catch(ExecutionException e){
			log.error("Erreur: ", e);
		}
		
		return null;
	}
	
	
	/*
	 * (non-Javadoc)
	 * @see org.springframework.beans.factory.DisposableBean#destroy()
	 */
	@Override
	public void destroy() throws Exception{
		if(ntsExecutors != null){
			try{
				// On arrète le pool de thread
				ntsExecutors.shutdown();
				
				if(ntsExecutors.isTerminated()){
					log.info("Le pool de thread a bien été arrété.");
				}else{
					if(!ntsExecutors.awaitTermination(10, TimeUnit.SECONDS)){
						List<SimpleTask<?>> listOfNonStoppedTasks = ntsExecutors.shutdownPool();
						for(SimpleTask<?> aTask: listOfNonStoppedTasks){
							log.warn("La tâche {} n'a pas pu être arrêtée.", aTask);
						}
					}
				}
			}catch(Throwable e){
				log.error("Erreur dans la fermeture du pool de thread: ", e);
			}
		}
	}

	
	// GETTER / SETTER
	public void setNbThreadsInPool(int nbThreadsInPool){
		this.nbThreadsInPool = nbThreadsInPool;
	}
}